﻿// HSS.ServiceModel.VirtualServiceHostFactory.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       VirtualServiceHostFactory.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/04/2010
// ----------------------------------------------------------------------------
namespace HSS.ServiceModel
{
	#region Using Directives
	using System;
	using System.ServiceModel;
	using System.ServiceModel.Activation;
	using System.ServiceModel.Channels;
	using System.ServiceModel.Description;
	using System.Web;
	using HSS.ServiceModel.Internal;
	#endregion

	#region VirtualServiceHostFactory
	/// <summary>
	/// VirtualServiceHostFactory implements a custom <see cref="ServiceHostFactory"/> for Virtual Wcf Services.
	/// </summary>
#if !DEBUG
	[System.Diagnostics.DebuggerStepThroughAttribute()]
#endif
	public sealed class VirtualServiceHostFactory : ServiceHostFactory
	{
		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		public VirtualServiceHostFactory()
		{
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Creates a System.ServiceModel.ServiceHost for a specified type of 
		/// service with a specific base address.
		/// </summary>
		/// <param name="serviceType">Specifies the type of service to host.</param>
		/// <param name="baseAddresses">The System.Array of type System.Uri that contains the base addresses for the service hosted.</param>
		/// <returns>A System.ServiceModel.ServiceHost for the type of service specified with a specific base address.</returns>
		protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
		{
			#region Create Host

			var request = HttpContext.Current.Request;
			var baseUri = default(Uri);
			var isbased = false;
			foreach (var uri in baseAddresses)
			{
				if (uri.IsBaseOf(request.Url))
				{
					baseUri = uri;
					isbased = true;
					break;
				}
			}
			if (!isbased)
				throw new UnauthorizedAccessException();

			if (VirtualServiceProvider.RequiresSSL && !request.IsSecureConnection)
				throw new UnauthorizedAccessException();

			var serviceContractType = this.GetServiceContractType(serviceType);

			ServiceHost host = null;

			var debug = false;
			if (null != HttpContext.Current)
				debug = HttpContext.Current.IsDebuggingEnabled;

			var vdir = string.Empty;
			var keepAlive = true;
			if (baseUri.Segments.Length > 1)
				vdir = baseUri.Segments[1];
			if (!string.IsNullOrEmpty(vdir))
				keepAlive = VirtualServiceProvider.KeepAliveEnabled(vdir);

			host = new ServiceHost(serviceType, baseAddresses);

			#endregion

			#region Configure Behaviors

			host.Description.Behaviors.Add(new VirtualInspectorBehavior(serviceContractType.Name, new VirtualServiceValidator(serviceType)));

			if (debug)
			{
				var metadataBehavior = new ServiceMetadataBehavior();
				if (baseUri.Scheme == Uri.UriSchemeHttps)
					metadataBehavior.HttpsGetEnabled = debug;
				else
					metadataBehavior.HttpGetEnabled = debug;
				host.Description.Behaviors.Remove<ServiceMetadataBehavior>();
				host.Description.Behaviors.Add(metadataBehavior);
			}

			var serviceDebug = host.Description.Behaviors[typeof(ServiceDebugBehavior)] as ServiceDebugBehavior;
			if (null != serviceDebug)
				serviceDebug.IncludeExceptionDetailInFaults = true;

			#endregion

			#region Add EndPoint
			host.AddServiceEndpoint(serviceContractType, GetBinding(baseUri.Scheme, keepAlive), "");
			host.Description.Endpoints[0].Behaviors.Add(new VirtualFaultBehavior());
			#endregion

			return host;
		}
		#endregion

		#region Methods
		private Type GetServiceContractType(Type serviceType)
		{
			Type serviceContractType = null;

			var atts = (ServiceContractAttribute[])serviceType.GetCustomAttributes(typeof(ServiceContractAttribute), false);
			if (null != atts && atts.Length == 0)
			{
				var interfaces = serviceType.GetInterfaces();
				if (null != interfaces && interfaces.Length > 0)
				{
					for (int i = 0; i < interfaces.Length; i++)
					{
						var interfaceX = interfaces[i];
						atts = (ServiceContractAttribute[])interfaceX.GetCustomAttributes(typeof(ServiceContractAttribute), false);
						if (null != atts && atts.Length == 1)
						{
							serviceContractType = interfaceX;
							break;
						}
					}
				}
			}

			if (null == serviceContractType)
				serviceContractType = serviceType;

			return serviceContractType;
		}
		private CustomBinding GetBinding(string scheme, bool keepAlive)
		{
			BasicHttpSecurityMode mode;
			if (scheme == Uri.UriSchemeHttp)
				mode = BasicHttpSecurityMode.None;
			else
				mode = BasicHttpSecurityMode.Transport;

			BindingElementCollection elements = new BindingElementCollection();
			elements.Add(GetEncoding());
			elements.Add(GetTransport(mode, keepAlive));

			return new CustomBinding(elements.Clone()) { Name = "binaryHttpBinding" };
		}
		private HttpTransportBindingElement GetTransport(BasicHttpSecurityMode mode, bool keepAlive)
		{
			HttpTransportBindingElement transport;
			if (mode != BasicHttpSecurityMode.Transport)
				transport = new HttpTransportBindingElement();
			else
				transport = new HttpsTransportBindingElement();
			transport.BypassProxyOnLocal = true;
			transport.KeepAliveEnabled = keepAlive;
			transport.MaxReceivedMessageSize = 0x7fffffffL;
			return transport;
		}
		private BinaryMessageEncodingBindingElement GetEncoding()
		{
			BinaryMessageEncodingBindingElement encoding = new BinaryMessageEncodingBindingElement();
			SetReaderQuotas(encoding.ReaderQuotas);
			return encoding;
		}
		private void SetReaderQuotas(System.Xml.XmlDictionaryReaderQuotas readerQuotas)
		{
			readerQuotas.MaxArrayLength = 0x7fffffff;
			readerQuotas.MaxBytesPerRead = 0x7fffffff;
			readerQuotas.MaxDepth = 0x7fffffff;
			readerQuotas.MaxNameTableCharCount = 0x7fffffff;
			readerQuotas.MaxStringContentLength = 0x7fffffff;
		}
		#endregion
	}
	#endregion
}